package com.jcommander.controller;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
import javax.swing.table.TableColumn;

import org.apache.commons.io.FileUtils;

import com.jcommander.JCommander;
import com.jcommander.gui.table.FileComparator;
import com.jcommander.gui.table.FileSystemTableModel;
import com.jcommander.model.FileSystemNode;
import com.jcommander.model.FileSystemRow;
import com.jcommander.model.IconLabel;
import com.jcommander.utils.CopyFileTask;
import com.jcommander.utils.ImageLoader;



public class FileSystemManager {
	
	private static final int ID_COLUMN_INDEX = 0;
	private static final String PARENT_DIRECTORY_LABEL = "[..]";
	
	private final JTable table;
	private final FileSystemTableModel tableModel;
	
	private FileSystemNode currentNode;
	private Map<Integer, FileSystemNode> currentNodeChildrens = new HashMap<Integer, FileSystemNode>();
	
	private FileSystemManager friendManager;
	private ProgressMonitor progressMonitor;
	
	public FileSystemManager(JTable table) {
		this.table = table;
		this.tableModel = (FileSystemTableModel)table.getModel();
		hideIdColumn();
		
	}
	
	public void fireDiskListChange(JComboBox comboBox) {
		final FileSystemNode selectedNode = (FileSystemNode)comboBox.getSelectedItem();
		changeNode(selectedNode);
		refreshFileSystemList();	
	}
	
	public void fireChangeDirectory() {
		int id = Integer.parseInt(table.getValueAt(table.getSelectedRow(), ID_COLUMN_INDEX).toString());
		FileSystemNode selectedNode = currentNodeChildrens.get(id);
		if ((id == 0) && (selectedNode == currentNode)) {//parent directory id
			selectedNode = new FileSystemNode(selectedNode.getParent());
		}
		if(selectedNode.isLeafFile()) {
			return;
		}
		changeNode(selectedNode);
		refreshFileSystemList();
	}
	
	public void fireCreateNewFolder() {
		String dirName = (String)JOptionPane.showInputDialog(JCommander.getFrame(), "New folder (directory)", "JCommander",
				JOptionPane.PLAIN_MESSAGE, new ImageIcon(), null, "New folder"); 
		//If a string was returned, say so.
		if ((dirName != null) && (dirName.length() > 0)) {
		    String dirPath = getCurrentPath() + dirName;
		    File file = new File(dirPath);
		    if (!file.exists()) {
		    	if (file.mkdir()) {
		    		refreshFileSystemList();
		    	}
		    } else {
		    	JOptionPane.showMessageDialog(JCommander.getFrame(), "Error: Directory ["+ dirName +"] " +
		    			"already exists. \nPlease specify different name.", "JCommander", JOptionPane.WARNING_MESSAGE);
		    }
		}
	}
	
	public void fireDeleteFile() {
		FileSystemNode selectedNode = getSelectedNode();
		String[] options = {"Yes", "No", "Cancel"};
		int selectedOption = JOptionPane.showOptionDialog(JCommander.getFrame(), "Do you really want to delete the " +
				"selected directory " + selectedNode.getFile().getName() + "?", "JCommander", 
				JOptionPane.YES_NO_CANCEL_OPTION,JOptionPane.QUESTION_MESSAGE, null, options,
			    options[2]);
		if (selectedOption == 0) {
			if (FileUtils.deleteQuietly(selectedNode.getFile())) {
				refreshFileSystemList();
			}
		}
	}
	
	public void fireMoveFile() {
		final File src = getSelectedNode().getFile();
		final File dest = new File(friendManager.getCurrentPath() + "/" + src.getName());
		progressMonitor = new ProgressMonitor(JCommander.getFrame(), "Moving",
                "", 0, 100);
		progressMonitor.setMillisToDecideToPopup(0);
		progressMonitor.setProgress(0);
		
		ActionListener copyDoneListener = new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				if (src.isDirectory()) {
					FileUtils.deleteQuietly(src);
				} else {
					src.delete();
				}
				refreshFileSystem();
				friendManager.refreshFileSystem();
			}
		};	
		copyFile(src, dest, copyDoneListener);
	}
	
	public void fireCopyFile() {
		final File src = getSelectedNode().getFile();
		final File dest = new File(friendManager.getCurrentPath() + "/" + src.getName());
		progressMonitor = new ProgressMonitor(JCommander.getFrame(), "Copying",
                "", 0, 100);
		progressMonitor.setMillisToDecideToPopup(0);
		progressMonitor.setProgress(0);
		
		ActionListener copyDoneListener = new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				friendManager.refreshFileSystem();
			}
		};	
		copyFile(src, dest, copyDoneListener);
	}
	
	public String getPath(FileSystemNode node) {
		return node.getFile().getAbsolutePath();
	}
	
	public String getCurrentPath() {
		return getPath(currentNode);
	}
	
	private FileSystemNode getSelectedNode() {
		int id = Integer.parseInt(table.getValueAt(table.getSelectionModel().getLeadSelectionIndex(), 
				ID_COLUMN_INDEX).toString());
		return currentNodeChildrens.get(id);
	}
	
	public void setFriendManager(FileSystemManager manager) {
		friendManager = manager;
	}
	
	public void refreshFileSystem() {
		refreshFileSystemList();
	}
	
	private void copyFile(File src, File dest, ActionListener copyDoneListener) {
		final CopyFileTask copyFileTask = new CopyFileTask(src, dest);
		copyFileTask.addDoneActionListener(copyDoneListener);
		copyFileTask.addPropertyChangeListener(new PropertyChangeListener() {

			public void propertyChange(PropertyChangeEvent event) {
				if ("progress" == event.getPropertyName()) {
					int progress = (Integer) event.getNewValue();
					progressMonitor.setProgress(progress);
					String message = String.format("Completed %d%%.\n",
							progress);
					progressMonitor.setNote(message);
					if (progressMonitor.isCanceled() || copyFileTask.isDone()) {
						Toolkit.getDefaultToolkit().beep();
						if (progressMonitor.isCanceled()) {
							copyFileTask.cancel(true);
						}
					}
				}
			}
		});
		copyFileTask.execute();
	}
	
	private void hideIdColumn() {
		//hide firstColumn because it is ID
		TableColumn idColumn = table.getColumnModel().getColumn(ID_COLUMN_INDEX);
		idColumn.setMinWidth(0);
		idColumn.setPreferredWidth(0);
		idColumn.setMaxWidth(0);
		
	}
	
	private void changeNode(FileSystemNode selectedNode) {
		currentNode = selectedNode;
	}
	
	private void refreshFileSystemList() {
		currentNodeChildrens.clear();
		tableModel.clearData();
		new Thread(new Runnable() {
			
			public void run() {
				List<File> childrens = currentNode.getChildren();
				List<FileSystemRow> rows = new ArrayList<FileSystemRow>();
				int i = 0;
				if (!currentNode.isRootFile()) {
					currentNodeChildrens.put(new Integer(i++), currentNode);
					rows.add(createParentDirectoryRow());
				}
				Collections.sort(childrens, new FileComparator());
				for (File file : childrens) {
					currentNodeChildrens.put(new Integer(i++), new FileSystemNode(file));
				}
				for (Integer key : currentNodeChildrens.keySet()) {
					if (!currentNode.isRootFile() && key == 0) {
						continue;
					}
					FileSystemRow row = currentNodeChildrens.get(key).getFileSystemRow();
					row.setId(key);
					rows.add(row);
				}
				tableModel.addData(rows);
			
				SwingUtilities.invokeLater(new Runnable() {
					
					public void run() {
						table.setAutoCreateRowSorter(true);
						table.setFillsViewportHeight(true);
						table.repaint();
					}
				});
			}
		}).start();
	}
	
	private FileSystemRow createParentDirectoryRow() {
		Icon icon = ImageLoader.getInstance().loadImage("/images/uparrow.png");
		String label = PARENT_DIRECTORY_LABEL;
		return new FileSystemRow(new IconLabel(icon, label), "", "", "");
	}

}
